#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <nginx.h>
#include "ngx_ev.h"


static char *ngx_ev_core_server(ngx_conf_t *cf, ngx_command_t *cmd,void *conf);
static char *ngx_ev_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_ev_core_application(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static void *ngx_ev_core_create_main_conf(ngx_conf_t *cf);
static char *ngx_ev_core_init_main_conf(ngx_conf_t *cf, void *conf);
static void *ngx_ev_core_create_srv_conf(ngx_conf_t *cf);
static char *ngx_ev_core_merge_srv_conf(ngx_conf_t *cf, void *parent,void *child);
static void *ngx_ev_core_create_app_conf(ngx_conf_t *cf);
static char *ngx_ev_core_merge_app_conf(ngx_conf_t *cf, void *parent,void *child);



static ngx_command_t  ngx_ev_core_commands[] = {

    { ngx_string("server"),
    NGX_EV_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
    ngx_ev_core_server,
    0,
    0,
    0 },

	{ ngx_string("listen"),
	NGX_EV_SRV_CONF | NGX_CONF_1MORE,
	ngx_ev_core_listen,
	NGX_EV_SRV_CONF_OFFSET,
	0,
	NULL },

	{ ngx_string("application"),
	NGX_EV_SRV_CONF | NGX_CONF_BLOCK | NGX_CONF_TAKE1,
	ngx_ev_core_application,
	NGX_EV_SRV_CONF_OFFSET,
	0,
	NULL },
    ngx_null_command
};

static ngx_ev_module_t  ngx_ev_core_module_ctx = {
    NULL,                                   /* preconfiguration */
    NULL,                                   /* postconfiguration */
	ngx_ev_core_create_main_conf,           /* create main configuration */
	ngx_ev_core_init_main_conf,             /* init main configuration */
	ngx_ev_core_create_srv_conf,            /* create server configuration */
	ngx_ev_core_merge_srv_conf,             /* merge server configuration */
	ngx_ev_core_create_app_conf,            /* create app configuration */
	ngx_ev_core_merge_app_conf              /* merge app configuration */
};


ngx_module_t  ngx_ev_core_module = {
    NGX_MODULE_V1,
    &ngx_ev_core_module_ctx,                /* module context */
    ngx_ev_core_commands,                   /* module directives */
    NGX_EV_MODULE,                          /* module type */
    NULL,                                   /* init master */
    NULL,                                   /* init module */
    NULL,                                   /* init process */
    NULL,                                   /* init thread */
    NULL,                                   /* exit thread */
    NULL,                                   /* exit process */
    NULL,                                   /* exit master */
    NGX_MODULE_V1_PADDING
};


static char *
ngx_ev_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
	void                       	*mconf;
	ngx_uint_t                  m;
	ngx_conf_t                  pcf;
	ngx_module_t				**modules;
	ngx_ev_module_t				*module;
	ngx_ev_conf_ctx_t			*ctx, *ev_ctx;
	ngx_ev_core_srv_conf_t		*cscf, **cscfp;
	ngx_ev_core_main_conf_t		*cmcf;

	ctx = ngx_pcalloc(cf->pool, sizeof(ngx_ev_conf_ctx_t));
	if (ctx == NULL) {
		return NGX_CONF_ERROR;
	}

	ev_ctx = cf->ctx;
	ctx->main_conf = ev_ctx->main_conf;

	/* the server{}'s srv_conf */

	ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_ev_max_module);
	if (ctx->srv_conf == NULL) {
		return NGX_CONF_ERROR;
	}

	ctx->app_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_ev_max_module);
	if (ctx->app_conf == NULL) {
		return NGX_CONF_ERROR;
	}

#if (nginx_version >= 1009011)
	modules = cf->cycle->modules;
#else
	modules = ngx_modules;
#endif

	for (m = 0; modules[m]; m++) {
		if (modules[m]->type != NGX_EV_MODULE) {
			continue;
		}

		module = modules[m]->ctx;

		if (module->create_srv_conf) {
			mconf = module->create_srv_conf(cf);
			if (mconf == NULL) {
				return NGX_CONF_ERROR;
			}

			ctx->srv_conf[modules[m]->ctx_index] = mconf;
		}

		if (module->create_app_conf) {
			mconf = module->create_app_conf(cf);
			if (mconf == NULL) {
				return NGX_CONF_ERROR;
			}

			ctx->app_conf[modules[m]->ctx_index] = mconf;
		}
	}

	/* the server configuration context */

	cscf = ctx->srv_conf[ngx_ev_core_module.ctx_index];
	cscf->ctx = ctx;

	cmcf = ctx->main_conf[ngx_ev_core_module.ctx_index];

	cscfp = ngx_array_push(&cmcf->servers);
	if (cscfp == NULL) {
		return NGX_CONF_ERROR;
	}

	*cscfp = cscf;


	/* parse inside server{} */

	pcf = *cf;
	cf->ctx = ctx;
	cf->cmd_type = NGX_EV_SRV_CONF;

	ngx_conf_parse(cf, NULL);

	*cf = pcf;

	return NGX_CONF_OK;
}

static char *ngx_ev_core_application(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
	return NGX_CONF_OK;
}

static char *ngx_ev_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
	ngx_ev_core_main_conf_t		*cmcf;
	ngx_ev_listen_t				*ls;
	ngx_url_t					u;
	ngx_str_t					*value;
	ngx_uint_t					i;

	value = cf->args->elts;
	ngx_memzero(&u, sizeof(ngx_url_t));

	u.url = value[1];
	u.listen = 1;

	if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
		if (u.err) {
			ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
				"%s in \"%V\" of the \"listen\" directive",
				u.err, &u.url);
		}
		return NGX_CONF_ERROR;
	}

	cmcf = ngx_ev_conf_get_module_main_conf(cf, ngx_ev_core_module);
	ls = cmcf->listen.elts;

	ls = ngx_array_push(&cmcf->listen);
	if (ls == NULL) {
		return NGX_CONF_ERROR;
	}

	ngx_memzero(ls, sizeof(ngx_ev_listen_t));
	ngx_memcpy(ls->sockaddr, (u_char *)&u.sockaddr, u.socklen);

	ls->socklen = u.socklen;
	ls->wildcard = u.wildcard;
	ls->ctx = cf->ctx;

	for (i = 2; i < cf->args->nelts; i++) {

		if (ngx_strcmp(value[i].data, "udp") == 0) {
			ls->type = SOCK_DGRAM;
			continue;
		}
		if (ngx_strcmp(value[i].data, "tcp") == 0) {
			ls->bind = 1;
			continue;
		}
	}

	return NGX_CONF_OK;
}

static void *ngx_ev_core_create_main_conf(ngx_conf_t *cf)
{
	ngx_ev_core_main_conf_t * cmcf;

	cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_ev_core_main_conf_t));
	if (cmcf == NULL) {
		return NULL;
	}

	if (ngx_array_init(&cmcf->servers, cf->pool, 4,
		sizeof(ngx_ev_core_srv_conf_t *))
		!= NGX_OK)
	{
		return NULL;
	}

	if (ngx_array_init(&cmcf->listen, cf->pool, 4, 
		sizeof(ngx_ev_listen_t))
		!= NGX_OK)
	{
		return NULL;
	}
	return cmcf;
}
static char *ngx_ev_core_init_main_conf(ngx_conf_t *cf, void *conf)
{
	return NGX_CONF_OK;
}
static void *ngx_ev_core_create_srv_conf(ngx_conf_t *cf)
{
	ngx_ev_core_srv_conf_t * cscf;

	cscf = ngx_pcalloc(cf->pool, sizeof(ngx_ev_core_srv_conf_t));
	if (cscf == NULL) {
		return NULL;
	}
	return cscf;
}
static char *ngx_ev_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
	return NGX_CONF_OK;
}
static void *ngx_ev_core_create_app_conf(ngx_conf_t *cf)
{
	ngx_ev_core_app_conf_s * cacf;

	cacf = ngx_pcalloc(cf->pool, sizeof(ngx_ev_core_app_conf_s));
	if (cacf == NULL) {
		return NULL;
	}
	return cacf;
}
static char *ngx_ev_core_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{
	return NGX_CONF_OK;
}
